#if DIST_VER == 3

#include "cfg.h"
#include "common.h"
#include "dist.h"
#include "formula.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>

size_t raw_start_end(size_t begin, size_t end, const seg_info_t *results,
                     size_t len_result, size_t *index_end) {
  size_t it = begin;
  size_t index_start;

  if (it > 1 && (results + it - 1)->valid) {
    index_start = (results + it - 1)->start_index;
  } else {
    index_start = (results + it)->start_index;
  }

  it = end;
  if (it + 1 < len_result && (results + it + 1)->valid) {
    *index_end = (results + it + 1)->end_index;
  } else {
    *index_end = (results + it)->end_index;
  }

  return index_start;
}

static uint8_t _proc_append_always(const staging_data_t *_, uint64_t it_start) {
  NOTUSED(_);
  NOTUSED(it_start);

  return 1;
}

static uint8_t _find_sleep_range_cond(const seg_info_t *result) {
  return (result->hr_avg < SLEEP_HR_AVG_THRESHOLD &&
          result->hr_sd < SLEEP_HR_SD_THRESHOLD &&
          result->motion_avg < SLEEP_MOTION_AVG_THRESHOLD &&
          result->motion_sd < SLEEP_MOTION_SD_THRESHOLD) ||
         result->motion_avg < SLEEP_MOTION_AVG_THRESHOLD;
}

static uint8_t _find_sleep_start_end(uint8_t strict_mode,
                                     const smp_hr_t *hr_data, size_t len,
                                     size_t index_start, size_t index_end,
                                     size_t *out_start, size_t *out_end) {
#define ITER4_ALL_ANY(start, end, all_mt_th, any_mot_th, all, any, bot, top)   \
  for (size_t i = 0; i < len; i++) {                                           \
    const smp_hr_t *x = hr_data + i;                                           \
    if (!(start <= x->ts && x->ts <= end)) {                                   \
      continue;                                                                \
    }                                                                          \
    if (x->motion > all_mt_th) {                                               \
      all = 0;                                                                 \
      break;                                                                   \
    }                                                                          \
    top = i;                                                                   \
    if (x->motion <= any_mot_th) {                                             \
      any = 1;                                                                 \
      if (-1 == bot) {                                                         \
        bot = i;                                                               \
      }                                                                        \
    }                                                                          \
  }

#define MATCH_STEPS(bot, all, any, top, threshold)                             \
  (bot >= 0 && all && any &&                                                   \
   ((hr_data + top)->steps <= (hr_data + bot)->steps + threshold))

  NOTUSED(strict_mode);

  size_t find_start = index_start;
  ssize_t start_index = -1, end_index = -1;

  while (find_start < index_end) {
    uint64_t start_time = (hr_data + find_start)->ts;
    uint64_t end_time = start_time + MIN_JUDGMENT_SLEEP_TIME * 60 * 1000;
    uint64_t end_time2 = start_time + MIN_JUDGMENT_SLEEP_TIME_2 * 60 * 1000;

    uint8_t one_all = 1, one_any = 0;
    uint8_t two_all = 1, two_any = 0;
    ssize_t one_bot = -1, two_bot = -1;
    size_t one_top = 0, two_top = 0;

    ITER4_ALL_ANY(start_time, end_time, FIND_SLEEP_WAKE_MOTION_THRESHOLD,
                  SLEEP_WAKE_LEAST_MOTION_THRESHOLD, one_all, one_any, one_bot,
                  one_top)
    ITER4_ALL_ANY(start_time, end_time2, FIND_SLEEP_MOTION_THRESHOLD,
                  SLEEP_WAKE_LEAST_MOTION_THRESHOLD, two_all, two_any, two_bot,
                  two_top)

    if (MATCH_STEPS(one_bot, one_all, one_any, one_top,
                    FIND_SLEEP_WAKE_STEPS_DIFF_THRESHOLD)) {
      start_index = find_start;
      break;
    } else if (MATCH_STEPS(two_bot, two_all, two_any, two_top,
                           FIND_SLEEP_WAKE_STEPS_DIFF_THRESHOLD)) {
      start_index = find_start;
      break;
    } else {
      find_start++;
    }
  }

  size_t find_end = index_end;
  while (find_end > index_start) {
    uint64_t end_time = (hr_data + find_end)->ts;
    uint64_t start_time = end_time - MIN_JUDGMENT_SLEEP_TIME * 60 * 1000;
    uint64_t start_time2 = end_time - MIN_JUDGMENT_SLEEP_TIME_2 * 60 * 1000;

    uint8_t one_all = 1, one_any = 0;
    uint8_t two_all = 1, two_any = 0;
    ssize_t one_bot = -1, two_bot = -1;
    size_t one_top = 0, two_top = 0;

    ITER4_ALL_ANY(start_time, end_time, FIND_SLEEP_WAKE_MOTION_THRESHOLD,
                  SLEEP_WAKE_LEAST_MOTION_THRESHOLD, one_all, one_any, one_bot,
                  one_top)
    ITER4_ALL_ANY(start_time2, end_time, FIND_WAKE_MOTION_THRESHOLD,
                  SLEEP_WAKE_LEAST_MOTION_THRESHOLD, two_all, two_any, two_bot,
                  two_top)

    if (MATCH_STEPS(one_bot, one_all, one_any, one_top,
                    FIND_SLEEP_WAKE_STEPS_DIFF_THRESHOLD)) {
      end_index = find_end;
      break;
    } else if (MATCH_STEPS(two_bot, two_all, two_any, two_top,
                           FIND_SLEEP_WAKE_STEPS_DIFF_THRESHOLD)) {
      end_index = find_end;
      break;
    } else {
      find_end--;
    }
  }

  if (-1 == start_index || -1 == end_index || start_index >= end_index) {
    return 0;
  }

  *out_start = (size_t)start_index;
  *out_end = (size_t)end_index;
  return 1;
}

static uint8_t _sleep_staging_min_cond(const smp_hr_t *list, size_t count) {
  NOTUSED(list);

  return count < 20 / get_smp_interval();
}

static uint8_t _staging_type_cond(uint32_t last_hr) {
  NOTUSED(last_hr);
  return 1;
}

static void _split_list_by_time2(const smp_hr_t *assign_list, size_t count,
                                 uint32_t time_minute, size_t *result,
                                 size_t *out_len) {
  uint64_t filter_start_time = assign_list->ts;
  uint64_t end_time = (assign_list + count - 1)->ts;
  size_t start_index = 0, j = 0;

  while (filter_start_time <= end_time) {
    uint64_t filter_end_time = filter_start_time + time_minute * 60 * 1000;
    uint8_t hit = 0;

    for (size_t i = start_index; i < count; i++) {
      const smp_hr_t *x = assign_list + i;
      if (x->ts > filter_end_time) {
        start_index = i;
        *(result + (j++)) = i;
        break;
      }
      hit = 1;
    }

    filter_start_time = hit ? filter_end_time : (assign_list + start_index)->ts;
  }
  *out_len = j;
}

static void _cal_span(size_t begin, size_t end, const smp_hr_t *data,
                      seg_info_t *info) {
  size_t tot = end - begin;

  uint32_t *hr_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);
  uint32_t *hrv_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);
  uint32_t *motion_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);
  uint32_t *steps_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);

  for (size_t i = begin; i < end; i++) {
    const smp_hr_t *x = data + i;

    *(hr_list + i - begin) = x->rate;
    *(hrv_list + i - begin) = x->hrv;
    *(motion_list + i - begin) = x->motion;
    *(steps_list + i - begin) = x->steps;
  }

  info->start_index = begin;
  info->end_index = end - 1;
  info->hr_avg = avg_uint32_t(hr_list, tot);
  info->hr_sd = deviation_i32u(hr_list, tot);
  info->hrv_avg = avg_uint32_t(hrv_list, tot);
  info->hrv_sd = deviation_i32u(hrv_list, tot);
  info->motion_avg = avg_uint32_t(motion_list, tot);
  info->motion_sd = deviation_i32u(motion_list, tot);
  info->steps_sd = deviation_i32u(steps_list, tot);
  info->steps_diff = (int32_t)(steps_list[tot - 1] - steps_list[0]);
  info->valid = 1;

  free(hr_list);
  free(hrv_list);
  free(motion_list);
  free(steps_list);
}

static void _segment_standard_diviation(const smp_hr_t *hr_list, size_t count,
                                        uint32_t time_minute,
                                        seg_info_t *result_list,
                                        size_t *len_result) {
#define APPEND_NEW()                                                           \
  if (begin != end) {                                                          \
    _cal_span(begin, end, hr_list, result_list + j);                           \
  } else {                                                                     \
    memset(result_list + j, 0x00, sizeof(seg_info_t));                         \
  }                                                                            \
  j++

  size_t *split_list = (size_t *)malloc(sizeof(size_t) * count);

  size_t out_len;
  _split_list_by_time2(hr_list, count, time_minute, split_list, &out_len);

  size_t begin, end, i;
  size_t j = 0;

  for (i = 0; i < out_len; i++) {
    size_t item_idx = *(split_list + i);

    begin = 0 == i ? 0 : *(split_list + i - 1);
    end = item_idx;

    APPEND_NEW();
  }

  begin = *(split_list + i - 1);
  end = count;
  APPEND_NEW();

  *len_result = j;

  free(split_list);
}

static smp_hr_t *_pre_process(const smp_hr_t *src_list, size_t count,
                              size_t *dst_count) {
  *dst_count = count;

  /*
  对于v3
  在排序之后
  不再对list有STORE操作
   */
  return (smp_hr_t *)src_list;
}

uint8_t _is_wrist(const smp_hr_t *hr_list, const uint32_t *slist,
                  size_t count) {
  NOTUSED(hr_list);
  NOTUSED(slist);
  NOTUSED(count);

  return 1;
}

uint8_t _is_acc_functional(const smp_hr_t *hr_list, size_t count) {
  NOTUSED(hr_list);
  NOTUSED(count);
  return 1;
}

void _pre_release(smp_hr_t *p_list) { NOTUSED(p_list); }

uint8_t _count4wake() { return 0; }

dist_action_t ver_sel = {
    .pre_proc = _pre_process,
    .pre_release = _pre_release,
    .count4wake = _count4wake,
    .proc_cond = _proc_append_always,
    .sleep_range_cond = _find_sleep_range_cond,
    .find_sleep_start_end_impl = _find_sleep_start_end,
    .sleep_staging_min_cond = _sleep_staging_min_cond,
    .staging_type_cond = _staging_type_cond,
    .segment_calc = _segment_standard_diviation,
    .is_wear = _is_wrist,
    .is_acc_func = _is_acc_functional,
};

#endif

